òɾۿûѧϰʹá
ԭַhttps://www.joinquant.com/post/16022

ԭһ˵ʽ鵽ԭĺ߽ۡ


ԭĲԴ£

# 뺯
from jqdata import *
import datetime
import time
import talib

'''
ArrayManager:
ƴKߵ࣬òԻڲȵKɣڹ̶ʱbarڣʱ
ǰƽ̨δƳùܣԶArrayManagerʵ          
'''
class ArrayManager(object):
    
    # ʼ趨׼ȵ
    def __init__(self, size=100):
        # 趨ArrayĻС
        self.size = size  
        
        # ָָArrayֵʼK߰ʱָ
        # VarArraysָȫKߵݣֵʽţVarsǸKǰĻ
        self.Vars = {'close':False,
                    'open':False,
                    'high':False,
                    'low':False}
                    
        self.VarsArrays = {'close':np.zeros(size),
                          'open':np.zeros(size),
                          'high':np.zeros(size),
                          'low':np.zeros(size)}
    
    # ArrayγµbarݣָȶǻڸArrayм    
    def updateBarArray(self):
        for var in self.VarsArrays.keys():
            self.VarsArrays[var][0:self.size-1] = self.VarsArrays[var][1:self.size]
            self.VarsArrays[var][-1] = self.Vars[var]
    
    # »棨ΪArray׼        
    def updateBar(self,close,high,low,open):
        # Varsûݣ߱clearд
        if self.Vars['close'] == False:
            self.Vars['close'] = close
            self.Vars['high'] = high
            self.Vars['low'] = low
            self.Vars['open'] = open
        else:
            # ݣʵʱ
            self.Vars['close'] = close
            self.Vars['high'] = max(self.Vars['high'],high)
            self.Vars['low'] = min(self.Vars['low'],low)
            
   
    # Bar       
    def clear(self):
        self.Vars['open']= False
        self.Vars['close']= False
        self.Vars['high'] = False
        self.Vars['low']= False
    
    # ǰArray
    def exportArray(self,field):
        return self.VarsArrays[field]
    # ǰ  
    def exportVar(self,field):
        return self.Vars[field]
    
    # 10ڵ߼    
    def Highest_10(self,future):
        return self.VarsArrays['close'][-10*g.TodayBar[future]-1:-1].max()
    
    # 10ڵͼ    
    def Lowest_10(self,future):
        return self.VarsArrays['close'][-10*g.TodayBar[future]-1:-1].min()


def initialize(context):
    # ò
    set_parameter(context)
    # 趨׼ڶƷֵĻز⵱л׼ûвο
    set_benchmark('511880.XSHG')
    # ̬Ȩģʽ(ʵ۸)
    set_option('use_real_price', True)
    # ˵orderϵAPIıerror͵log
    log.set_level('order', 'error')
    ### ڻ趨 ###
    # 趨˻Ϊ˻
    set_subportfolios([SubPortfolioConfig(cash=context.portfolio.starting_cash, type='futures')])
    # ڻÿʽʱǣʱ֮1,ʱ֮1,ƽΪ֮1
    set_order_cost(OrderCost(open_commission=0.0001, close_commission=0.0001,close_today_commission=0.0001), type='index_futures')
    # 趨֤
    set_option('futures_margin_rate', 0.15)
    # û㣨5˫ǧ1
    set_slippage(PriceRelatedSlippage(0.001),type='future')
    # ǰ
    run_daily( before_market_open, time='before_open', reference_security='AG8888.XSGE')
    # ʱ
    run_daily( DataPrepare, time='every_bar', reference_security='AG8888.XSGE')
    # ̺
    run_daily( after_market_close, time='after_close', reference_security='AG8888.XSGE')
    #и
    
def set_parameter(context):
    ###############
    # ֵϢƷֱַӦ
    g.AM = {}  # ArrayManger
    g.Data = {}  # ֵ
    g.LastPrice = {}  # ¼۸ֵ䣨ڵֹ
    g.HighPrice = {} # Ʒ߼ֵ䣨ڵֹ
    g.LowPrice = {} # Ʒͼֵ䣨ڵֹ
    g.TradeLots = {}  # ƷֵĽϢ
    g.Price_dict = {} # Ʒּ۸бֵ
    g.Times = {} # ڷֹֹ룩
    g.TodayBar = {} # ÿfutureյbar
    g.MidLine = {} # ִ
    g.ATR = {} # ATR
    g.Reentry_long = False # ֹ
    g.Reentry_short = False # ֹ
    ###############
    g.Signal = 0 # жź
    g.offset = 2 # ͨ
    g.NATRstop = 4
    g.Window = 15
    g.MarginRate = 1
    g.MappingReal = {} # ʵԼӳ䣨keyΪsymbolvalueΪԼ
    g.MappingIndex = {} # ָԼӳ keyΪ symbolvalueΪָԼ
    # ׵ڻƷϢ
    g.instruments = ['JD','RU','RB','I','J','TA']
    #g.instruments = ['RB']
    # иKʱ䣬ÿʱиҹʱ䲻Ϊ̵һ15ʱУ
    g.CloseMarket = ['0915','1130','1500']
    # Bar㺯ʱ䣨ÿмᵼٶȡС15ӣ
    g.DatePrepareTime = ['00','15','30','45']
    #ʼݴfuture_list
    set_future_list(context)


    # ݳʼ
def set_future_list(context):
    for ins in g.instruments:
        idx = get_future_code(ins)
        dom = get_dominant_future(ins)
        # ӳֵ
        g.MappingIndex[ins] = idx
        g.MappingReal[ins] = dom
        g.Times[idx] = 0
          
                
## ǰк
def before_market_open(context):
    
    # ԼƷ֣벢޸ڻֵϢ
    for ins in g.instruments:
        RealFuture = get_dominant_future(ins)
        IndexFuture = get_future_code(ins)
        
        # ߼жϸƷǷҹ̣TodayBar
        g.Data[IndexFuture] = attribute_history(IndexFuture,120, unit='60m',fields = ['close'])
        HourList = [x.strftime('%H') for x in g.Data[IndexFuture].index[:]]
        if '21' in HourList: 
            g.TodayBar[IndexFuture] = 3
        else:
            g.TodayBar[IndexFuture] = 2
        
        
        if RealFuture == '':
            pass
        else:
            # жǷִreplace_old_futures
            if RealFuture == g.MappingReal[ins]:
                pass
            else:
                replace_old_futures(context,ins,RealFuture)
                g.HighPrice[IndexFuture] = False
                g.LowPrice[IndexFuture] = False
                g.Times[IndexFuture] = 0
                g.MappingReal[ins] = RealFuture
                
            # 㽻
            lots = context.portfolio.starting_cash/len(g.instruments)
            g.TradeLots[IndexFuture] = get_lots(lots,ins)

  
def DataPrepare(context):
    # ÿ15һݽӺģ
    if str(context.current_dt.time().strftime('%M')) in g.DatePrepareTime:
        for ins in g.instruments:
            IndexFuture = get_future_code(ins)
            RealFuture = get_dominant_future(ins)
            # ȡʷ
            g.LastPrice[IndexFuture] = attribute_history(IndexFuture,50, unit='15m',fields = ['close','open','high','low'])
            # ǰʱڸƷʵʽʱڣ15barִƴ
            if str(context.current_dt.time()) in [x.strftime('%H:%M:%S') for x in g.LastPrice[IndexFuture].index[:]]:
                # ʼ
                if IndexFuture not in g.AM.keys():
                    g.AM[IndexFuture] = ArrayManager()
                    g.AM[IndexFuture].updateBar(g.LastPrice[IndexFuture]['close'][-1],
                                            g.LastPrice[IndexFuture]['high'][-1],
                                            g.LastPrice[IndexFuture]['low'][-1],
                                            g.LastPrice[IndexFuture]['open'][-1])
                else:
                    # ʱʱBar
                    if g.LastPrice[IndexFuture].index[-1].strftime('%H%M') in g.CloseMarket :
                        # ûҹ̵Ʒ915ֲBar
                        if g.TodayBar[IndexFuture] == 2 and g.LastPrice[IndexFuture].index[-1].strftime('%H%M') == '0915':
                            pass
                        else:
                            # updateBarArrayȰ䵽ArrayУǰƶ1λ
                            # clearͬʱɾ
                            g.AM[IndexFuture].updateBarArray()
                            g.AM[IndexFuture].clear()
                            # ÿµBarʱִָ뽻߼
                            market_open(context)
                    # ʱ»棨4۸
                    else:
                        g.AM[IndexFuture].updateBar(g.LastPrice[IndexFuture]['close'][-1],
                                            g.LastPrice[IndexFuture]['high'][-1],
                                            g.LastPrice[IndexFuture]['low'][-1],
                                            g.LastPrice[IndexFuture]['open'][-1])


## ʱк
def market_open(context):
    # ʱ
    log.info('ʱ(market_open):'+str(context.current_dt.time()))
    # ѭڻƷ֣
    
    for ins in g.MappingReal.keys():
        # յBarȷATRĲ
        IndexFuture = get_future_code(ins)
        RealFuture = get_dominant_future(ins)

        # ATR  
        g.low = g.AM[IndexFuture].exportArray('low')[-g.TodayBar[IndexFuture]*g.Window-1:]
        g.high = g.AM[IndexFuture].exportArray('high')[-g.TodayBar[IndexFuture]*g.Window-1:]
        g.close = g.AM[IndexFuture].exportArray('close')[-g.TodayBar[IndexFuture]*g.Window-1:]
        # Ϊ˷ֹATR㣬ִĿݣ򲻼ATR
        if 0 in g.close:
            pass
        else:
            g.ATR[IndexFuture] = talib.ATR(g.high,g.low,g.close, timeperiod = g.TodayBar[IndexFuture]*g.Window)[-1]
            print(g.ATR[IndexFuture])
            # ִ
            g.MidLine = g.close[-g.Window*g.TodayBar[IndexFuture]:].mean()
            g.BollUp = g.MidLine + g.offset*g.ATR[IndexFuture]
            g.BollDown = g.MidLine - g.offset*g.ATR[IndexFuture]
            
        
            # źż
            if g.close[-1] > g.BollUp:
                g.Cross = 1
            elif g.close[-1] < g.BollDown:
                g.Cross = -1
            else:
                g.Cross = 0
            
            #жϽźţִͻ+ɶ볡
            if  g.Cross == 1 and g.Reentry_long == False:
                g.Signal = 1
            elif g.Cross == -1 and g.Reentry_short == False:
                g.Signal = -1
            else:
                g.Signal = 0
            # ִн
            Trade(context,RealFuture,IndexFuture)
            # зֹģ
            Dont_Re_entry(context,IndexFuture,ins)
            # +1
            g.Times[IndexFuture] += 1


## ̺к
def after_market_close(context):
    log.info(str('ʱ(after_market_close):'+str(context.current_dt.time())))
    # õгɽ¼
    trades = get_trades()
    for _trade in trades.values():
        log.info('ɽ¼'+str(_trade))
    log.info('һ')
    log.info('##############################################################')


## ģ 
def Trade(context,RealFuture,IndexFuture):
    
    # ߸ߣ׷ֹʧЧɿ
    if g.Signal == 1 and context.portfolio.long_positions[RealFuture].total_amount == 0:
        if context.portfolio.short_positions[RealFuture].total_amount != 0:
            log.info('ͷгֲ֣%s'%(RealFuture))
        order_target(RealFuture,0,side='short')
        order_target(RealFuture,g.TradeLots[IndexFuture],side='long')
        g.HighPrice[IndexFuture] = g.close[-1]
        g.LowPrice[IndexFuture] = False
        log.info('Լ%s'%(RealFuture))
    
    elif g.Signal == -1 and context.portfolio.short_positions[RealFuture].total_amount == 0:
        if context.portfolio.long_positions[RealFuture].total_amount != 0:
            log.info('ͷгֲ֣%s'%(RealFuture))
        order_target(RealFuture,0,side ='long')
        order_target(RealFuture,g.TradeLots[IndexFuture],side='short')
        g.LowPrice[IndexFuture] = g.close[-1]
        g.HighPrice[IndexFuture] = False
        log.info('պԼ%s'%(RealFuture))
    else:
        TrailingStop(context,RealFuture,IndexFuture)
        
        
# ֹֹģ
def Dont_Re_entry(context,IndexFuture,ins):
    # ģ飺һֹ10ڲףּ۸ͻ¼10ڵʧЧ
    g.Highest_high_10 = g.AM[IndexFuture].Highest_10(IndexFuture)
    g.Lowest_low_10 = g.AM[IndexFuture].Lowest_10(IndexFuture)
    
    if  g.Reentry_long == True:
        if g.Times[IndexFuture] > 10*g.TodayBar[IndexFuture] or g.close[-1] > g.Highest_high_10 :
            g.Reentry_long = False
    if  g.Reentry_short == True:
        if g.Times[IndexFuture] > 10*g.TodayBar[IndexFuture] or g.close[-1] < g.Lowest_low_10 :
            g.Reentry_short = False
        
        
# ֹģ
def TrailingStop(context,RealFuture,IndexFuture):
    
    # λ״̬
    long_positions = context.portfolio.long_positions
    short_positions = context.portfolio.short_positions
    # ͷ߼ ͷjinchanghou
    g.HighPrice[IndexFuture] = max(g.HighPrice[IndexFuture],g.close[-1])
    g.LowPrice[IndexFuture] = min(g.LowPrice[IndexFuture],g.close[-1])
    
    if RealFuture in long_positions.keys():
        if long_positions[RealFuture].total_amount > 0:
            if g.HighPrice[IndexFuture]:
                if g.close[-1]  < g.HighPrice[IndexFuture]  - g.NATRstop*g.ATR[IndexFuture]:
                    log.info('ͷֹ:\t' +  RealFuture)
                    order_target(RealFuture,0,side = 'long')
                    g.Reentry_long = True

    if RealFuture in short_positions.keys():
        if short_positions[RealFuture].total_amount > 0:
            if g.LowPrice[IndexFuture]:
                if g.close[-1]  > g.LowPrice[IndexFuture] + g.NATRstop*g.ATR[IndexFuture]:
                    log.info('ͷֹ:\t' + RealFuture)
                    order_target(RealFuture,0,side = 'short')
                    g.Reentry_short = True
        

# Ʋģ飺Լʱƽǰֲ֣ΪԼ        
def replace_old_futures(context,ins,RealFuture):
    
    LastFuture = g.MappingReal[ins]
    
    if LastFuture in context.portfolio.long_positions.keys():
        lots_long = context.portfolio.long_positions[LastFuture].total_amount
        order_target(LastFuture,0,side='long')
        order_target(RealFuture,lots_long,side='long')
        print('Լƽֻ²')
    
    if LastFuture in context.portfolio.short_positions.keys():
        lots_short = context.portfolio.short_positions[LastFuture].total_amount
        order_target(LastFuture,0,side='short')
        order_target(RealFuture,lots_short,side='short')
        print('Լƽղֻ²')

        

        
# ȡʱڽ׵ڻԼ
def get_future_code(symbol):
    future_code_list = {'A':'A8888.XDCE', 'AG':'AG8888.XSGE', 'AL':'AL8888.XSGE', 'AU':'AU8888.XSGE',
                        'B':'B8888.XDCE', 'BB':'BB8888.XDCE', 'BU':'BU8888.XSGE', 'C':'C8888.XDCE', 
                        'CF':'CF8888.XZCE', 'CS':'CS8888.XDCE', 'CU':'CU8888.XSGE', 'ER':'ER8888.XZCE', 
                        'FB':'FB8888.XDCE', 'FG':'FG8888.XZCE', 'FU':'FU8888.XSGE', 'GN':'GN8888.XZCE', 
                        'HC':'HC8888.XSGE', 'I':'I8888.XDCE', 'IC':'IC8888.CCFX', 'IF':'IF8888.CCFX', 
                        'IH':'IH8888.CCFX', 'J':'J8888.XDCE', 'JD':'JD8888.XDCE', 'JM':'JM8888.XDCE', 
                        'JR':'JR8888.XZCE', 'L':'L8888.XDCE', 'LR':'LR8888.XZCE', 'M':'M8888.XDCE', 
                        'MA':'MA8888.XZCE', 'ME':'ME8888.XZCE', 'NI':'NI8888.XSGE', 'OI':'OI8888.XZCE', 
                        'P':'P8888.XDCE', 'PB':'PB8888.XSGE', 'PM':'PM8888.XZCE', 'PP':'PP8888.XDCE', 
                        'RB':'RB8888.XSGE', 'RI':'RI8888.XZCE', 'RM':'RM8888.XZCE', 'RO':'RO8888.XZCE', 
                        'RS':'RS8888.XZCE', 'RU':'RU8888.XSGE', 'SF':'SF8888.XZCE', 'SM':'SM8888.XZCE', 
                        'SN':'SN8888.XSGE', 'SR':'SR8888.XZCE', 'T':'T8888.CCFX', 'TA':'TA8888.XZCE', 
                        'TC':'TC8888.XZCE', 'TF':'TF8888.CCFX', 'V':'V8888.XDCE', 'WH':'WH8888.XZCE', 
                        'WR':'WR8888.XSGE', 'WS':'WS8888.XZCE', 'WT':'WT8888.XZCE', 'Y':'Y8888.XDCE', 
                        'ZC':'ZC8888.XZCE', 'ZN':'ZN8888.XSGE'}
    try:
        return future_code_list[symbol]
    except:
        return 'WARNING: ޴˺Լ'


# ȡATRͷ磩
def get_lots(cash,symbol):
    future_coef_list = {'A':10, 'AG':15, 'AL':5, 'AU':1000,
                        'B':10, 'BB':500, 'BU':10, 'C':10,
                        'CF':5, 'CS':10, 'CU':5, 'ER':10,
                        'FB':500, 'FG':20, 'FU':50, 'GN':10,
                        'HC':10, 'I':100, 'IC':200, 'IF':300,
                        'IH':300, 'J':100, 'JD':5, 'JM':60,
                        'JR':20, 'L':5, 'LR':10, 'M':10,
                        'MA':10, 'ME':10, 'NI':1, 'OI':10,
                        'P':10, 'PB':5, 'PM':50, 'PP':5,
                        'RB':10, 'RI':20, 'RM':10, 'RO':10,
                        'RS':10, 'RU':10, 'SF':5, 'SM':5,
                        'SN':1, 'SR':10, 'T':10000, 'TA':5,
                        'TC':100, 'TF':10000, 'V':5, 'WH':20,
                        'WR':10, 'WS':50, 'WT':10, 'Y':10,
                        'ZC':100, 'ZN':5,'AP':10}
    RealFuture = get_dominant_future(symbol)
    IndexFuture = get_future_code(symbol)
    # ȡ۸list
    Price_dict = attribute_history(IndexFuture,10,'1d',['open'])
    # ûݣ
    if len(Price_dict) == 0:
        return
    else:
        open_future = Price_dict.iloc[-1]
    # 
    if IndexFuture in g.ATR.keys():
        # ÿʹ5%ʽ𿪲ֽ
        # Լֵıʽǣg.ATR[IndexFuture]*future_coef_list[symbol]
        return cash*0.05/(g.ATR[IndexFuture]*future_coef_list[symbol])
    else:# ֮ûfutureдATRֵ䵱е
        return 0


'''
# ȡ(ATR汾
def get_lots(cash,symbol):
    # ȡԼģ(Contract Size)Ҳƽ׵λ
    future_Contract_Size = {'A':10, 'AG':15, 'AL':5, 'AU':1000,
                        'B':10, 'BB':500, 'BU':10, 'C':10,
                        'CF':5, 'CS':10, 'CU':5, 'ER':10,
                        'FB':500, 'FG':20, 'FU':50, 'GN':10,
                        'HC':10, 'I':100, 'IC':200, 'IF':300,
                        'IH':300, 'J':100, 'JD':5, 'JM':60,
                        'JR':20, 'L':5, 'LR':10, 'M':10,
                        'MA':10, 'ME':10, 'NI':1, 'OI':10,
                        'P':10, 'PB':5, 'PM':50, 'PP':5,
                        'RB':10, 'RI':20, 'RM':10, 'RO':10,
                        'RS':10, 'RU':10, 'SF':5, 'SM':5,
                        'SN':1, 'SR':10, 'T':10000, 'TA':5,
                        'TC':100, 'TF':10000, 'V':5, 'WH':20,
                        'WR':10, 'WS':50, 'WT':10, 'Y':10,
                        'ZC':100, 'ZN':5,'AP':10}
    future = get_dominant_future(symbol)
    # ȡ۸list
    Price_dict = attribute_history(future,10,'1d',['open'])
    # ûݣ
    if len(Price_dict) == 0:
        return
    else:
        # ¿̼ۣܹµ
        open_price = Price_dict.iloc[-1]
    # ۸*Լģ=ֵ
    # ֤ʹã33%
    # Լ֤ıʽǣopen_price*future_Contract_Size[symbol]*g.MarginRate
    return cash*0.33/(open_price*future_Contract_Size[symbol]*g.MarginRate)
'''
